package com.auth.config.security;

import com.auth.config.code.RedisAuthorizationCodeServices;
import com.redis.RedisUtil;
import com.utility.constant.Constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.util.Arrays;

/**
 * 使用Jwt存储token的配置
 * @author: hw
 * @date: 2020/12/3 13:54
 */
@Configuration
public class JwtTokenStoreConfig {

    @Autowired
    private LettuceConnectionFactory lettuceConnectionFactory;

    @Autowired
    private RedisUtil  redisUtil;

    @Autowired
    private SecretKey secretKey;

    @Bean
    public TokenStore tokenStore(){
        RedisTokenStore redisTokenStore = new RedisTokenStore(lettuceConnectionFactory);
        redisTokenStore.setSerializationStrategy(new CustomSerializationStrategy());
        return redisTokenStore;
    }

    @Bean
    public AuthorizationCodeServices redisAuthorizationCodeServices(){
        // 设置授权码模式的授权码如何存取，暂时采用内存方式
        return new RedisAuthorizationCodeServices(redisUtil);
    }


    /**
     * Jwt令牌实现根据Oauth身份生成令牌
     *      如果已有令牌也可以当TokenEnhancer使用
     *          加密方式：使用非对称加密使用
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() throws IOException {

        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        // jks 别名
        accessTokenConverter.setKeyPair(keyPair());
        // 公钥
        Resource resource = new ClassPathResource(secretKey.getJksPublicName());

        StringBuffer publicKey  = new StringBuffer();
        try( BufferedReader in = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
            String line = "";
            while ((line = in.readLine()) != null) {
                publicKey .append(line);
            }
        }
        accessTokenConverter.setVerifierKey(publicKey.toString());
        return accessTokenConverter;
    }

    /**
     * 一个密钥对(一个公钥和一个密钥)的简单持有者
     * @return
     */
    @Bean
    public KeyPair keyPair(){
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(
                new ClassPathResource(secretKey.getJksName()),secretKey.getJksPassword().toCharArray());
        return keyStoreKeyFactory.getKeyPair(secretKey.getJksAlias());
    }



    /**
     * 自定义令牌增强
     * @return
     */
    @Bean
    public TokenEnhancer customTokenEnhancer(){
        return new CustomTokenEnhancer();
    }



    /**
     * 令牌的服务实现
     *      {@linkplain AuthorizationServerTokenServices 授权服务器令牌服务类接口} 默认实现 DefaultTokenServices
     *      可以自己定义一些参数
     *     {@linkplain TokenEnhancer 令牌增强接口，使用例如jwt样式令牌格式，或者自定义令牌参数,可以有多个实现组合}
     * @return
     */
    @Bean
    @Primary
    public DefaultTokenServices defaultTokenServices() throws IOException{
        DefaultTokenServices service = new DefaultTokenServices();
        //支持刷新令牌
        service.setSupportRefreshToken(true);
        //令牌存储策略
        service.setTokenStore(tokenStore());
        //增强令牌策略链
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(),customTokenEnhancer()));
        service.setTokenEnhancer(tokenEnhancerChain);
        // 令牌默认有效期2小时
        service.setAccessTokenValiditySeconds(Constant.TOKEN_VALID);
        // 刷新令牌默认有效期3天 return service;
        service.setRefreshTokenValiditySeconds(Constant.TOKEN_REFRESH);
        return service;
    }

}

